package by.yakusekmaxim.bookgid.dataaccess.impl;

import java.util.List;

import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Root;
import javax.persistence.metamodel.SingularAttribute;

import org.hibernate.jpa.criteria.OrderImpl;
import org.springframework.stereotype.Repository;

import by.yakusekmaxim.bookgid.dataaccess.BookDao;
import by.yakusekmaxim.bookgid.dataaccess.util.Filter;
import by.yakusekmaxim.bookgid.datamodel.Book;
import by.yakusekmaxim.bookgid.datamodel.Book_;
import by.yakusekmaxim.bookgid.datamodel.enums.PublicationStatus;

@Repository
public class BookDaoImpl extends AbstractDaoImpl<Long, Book> implements BookDao {

	protected BookDaoImpl() {
		super(Book.class);
	}

	@Override
	public List<Book> getAllBooks() {
		CriteriaBuilder cBuilder = getEm().getCriteriaBuilder();

		CriteriaQuery<Book> criteria = cBuilder.createQuery(Book.class);
		Root<Book> root = criteria.from(Book.class);

		criteria.select(root);

		TypedQuery<Book> query = getEm().createQuery(criteria);
		List<Book> results = query.getResultList();
		return results;
	}

	@Override
	public List<Book> getAllBooks(SingularAttribute<Book, ?> attribute ,Object value) {
		CriteriaBuilder cBuilder = getEm().getCriteriaBuilder();

		CriteriaQuery<Book> criteria = cBuilder.createQuery(Book.class);
		Root<Book> root = criteria.from(Book.class);
		criteria.where(cBuilder.equal(root.get(attribute), value));
		criteria.select(root);

		TypedQuery<Book> query = getEm().createQuery(criteria);
		List<Book> results = query.getResultList();
		return results;
	}

	@Override
	public Long getCount() {
		CriteriaBuilder cBuilder = getEm().getCriteriaBuilder();

		CriteriaQuery<Long> criteria = cBuilder.createQuery(Long.class);
		Root<Book> root = criteria.from(Book.class);

		criteria.select(cBuilder.count(root));

		TypedQuery<Long> query = getEm().createQuery(criteria);
		return query.getSingleResult();
	}

	/*
	 * 'select all' with sorting and paging example.
	 */
	@Override
	public List<Book> getAllBooks(SingularAttribute<Book, ?> attr,
			boolean ascending, int startRecord, int pageSize) {
		CriteriaBuilder cBuilder = getEm().getCriteriaBuilder();

		CriteriaQuery<Book> criteria = cBuilder.createQuery(Book.class);
		Root<Book> root = criteria.from(Book.class);

		criteria.select(root);
		criteria.orderBy(new OrderImpl(root.get(attr), ascending));

		TypedQuery<Book> query = getEm().createQuery(criteria);
		query.setFirstResult(startRecord);
		query.setMaxResults(pageSize);

		List<Book> results = query.getResultList();
		return results;
	}

	@Override
	public List<Book> getAllBooksByName(String name) {
		CriteriaBuilder cBuilder = getEm().getCriteriaBuilder();

		CriteriaQuery<Book> root = cBuilder.createQuery(Book.class);
		Root<Book> criteria = root.from(Book.class);

		root.select(criteria);

		root.where(cBuilder.equal(criteria.get(Book_.name), name));

		TypedQuery<Book> query = getEm().createQuery(root);
		List<Book> results = query.getResultList();
		return results;
	}

	@Override
	public List<Book> getAllProductsWithImages() {
		CriteriaBuilder cBuilder = getEm().getCriteriaBuilder();

		CriteriaQuery<Book> criteria = cBuilder.createQuery(Book.class);
		Root<Book> root = criteria.from(Book.class);

		criteria.select(root);
		root.fetch(Book_.image);
		criteria.distinct(true);

		TypedQuery<Book> query = getEm().createQuery(criteria);
		List<Book> results = query.getResultList();
		return results;
	}

	@Override
	public Long getCount(SingularAttribute<Book, ?> attribute, Object value) {
		CriteriaBuilder cBuilder = getEm().getCriteriaBuilder();

		CriteriaQuery<Long> criteria = cBuilder.createQuery(Long.class);
		Root<Book> root = criteria.from(Book.class);
		criteria.where(cBuilder.equal(root.get(attribute), value));
		criteria.select(cBuilder.count(root));

		TypedQuery<Long> query = getEm().createQuery(criteria);
		return query.getSingleResult();
	}

	@Override
	public List<Book> getAllBook(SingularAttribute<Book, ?> attribute,
			boolean ascending, int startRecord, int pageSize) {
		CriteriaBuilder cBuilder = getEm().getCriteriaBuilder();

		CriteriaQuery<Book> criteria = cBuilder.createQuery(Book.class);
		Root<Book> root = criteria.from(Book.class);

		criteria.select(root);
		criteria.orderBy(new OrderImpl(root.get(attribute), ascending));

		TypedQuery<Book> query = getEm().createQuery(criteria);
		query.setFirstResult(startRecord);
		query.setMaxResults(pageSize);

		List<Book> results = query.getResultList();
		return results;
	}

	@Override
	public List<Book> getAllBook(SingularAttribute<Book, ?> attribute,
			boolean ascending, int startRecord, int pageSize,
			Filter<Book> filter) {
		SingularAttribute<Book, ?> attributeF = filter.getAttribute();
		Object valueF = filter.getValue();

		CriteriaBuilder cBuilder = getEm().getCriteriaBuilder();

		CriteriaQuery<Book> criteria = cBuilder.createQuery(Book.class);
		Root<Book> root = criteria.from(Book.class);
		criteria.where(cBuilder.equal(root.get(attributeF), valueF));
		criteria.select(root);
		criteria.orderBy(new OrderImpl(root.get(attribute), ascending));

		TypedQuery<Book> query = getEm().createQuery(criteria);
		query.setFirstResult(startRecord);
		query.setMaxResults(pageSize);

		List<Book> results = query.getResultList();
		return results;
	}
	
	
	@Override
	public List<Book> getAllPublicBook(SingularAttribute<Book, ?> attribute,
			boolean ascending, int startRecord, int pageSize) {
		CriteriaBuilder cBuilder = getEm().getCriteriaBuilder();

		CriteriaQuery<Book> criteria = cBuilder.createQuery(Book.class);
		Root<Book> root = criteria.from(Book.class);
		criteria.where(cBuilder.equal(root.get(Book_.status), PublicationStatus.publicationstatus_published));
		criteria.select(root);
		criteria.orderBy(new OrderImpl(root.get(attribute), ascending));

		TypedQuery<Book> query = getEm().createQuery(criteria);
		query.setFirstResult(startRecord);
		query.setMaxResults(pageSize);

		List<Book> results = query.getResultList();
		return results;
	}

	@Override
	public List<Book> getAllPublicBook(SingularAttribute<Book, ?> attribute,
			boolean ascending, int startRecord, int pageSize,
			Filter<Book> filter) {
		SingularAttribute<Book, ?> attributeF = filter.getAttribute();
		Object valueF = filter.getValue();

		CriteriaBuilder cBuilder = getEm().getCriteriaBuilder();

		CriteriaQuery<Book> criteria = cBuilder.createQuery(Book.class);
		Root<Book> root = criteria.from(Book.class);
		criteria.where(cBuilder.equal(root.get(attributeF), valueF),
				cBuilder.equal(root.get(Book_.status), PublicationStatus.publicationstatus_published));
		criteria.select(root);
		criteria.orderBy(new OrderImpl(root.get(attribute), ascending));

		TypedQuery<Book> query = getEm().createQuery(criteria);
		query.setFirstResult(startRecord);
		query.setMaxResults(pageSize);

		List<Book> results = query.getResultList();
		return results;
	}
	

}
